package logic;
import java.util.Iterator;

import logic.GraphProcessing.DepthFirstIterator;

import data.Node;
import data.Vector2;

/**
 * class, that performs the animation by setting the euclidian coordinates of the nodes of the radial layout and defines ring styles
 * @author Fennes
 *
 */
public class Visualization extends Thread{
	//private LinkedList<Node> nodeList;
	private static Node focusNode;
	private static GraphProcessing graphProcessing = new GraphProcessing();
	private static long animationTiming = 5000000000L;
	private static float ringScaling = 2;
	private static float ringDisort = 1;
	private static boolean isBusy;
	/**
	 * performs the animation
	 */
	public void run(){
		while(isBusy)
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		isBusy = true;
		if(focusNode == null)
			return;
		graphProcessing.generateTree(focusNode);
		long currentTime;
		Iterator<Node>iterator;
		Node node;
		double animationProgress; //between 0 and 1
		double angle;
		double radius;
		long animationStartTime = System.nanoTime();
		long tempAnimationTiminig = animationTiming;
		while(animationStartTime+tempAnimationTiminig > (currentTime = System.nanoTime())){
			animationProgress = Math.atan((currentTime-animationStartTime)/(double)tempAnimationTiminig*Math.PI*6-Math.PI*3)/2.95+0.5;
			//System.out.println(animationProgress);
			iterator = graphProcessing.iteratorDepthFirst();
			//iterator = nodeList.iterator();
			while(iterator.hasNext()){
				
			//interpolate polar coordinates of the nodes and compute euclidian coordinates
				node = iterator.next();
				//correct angle animation while 0-traverse
				if(Math.abs(node.getAngle()-node.getOldAngle())>Math.PI)
					if(node.getOldAngle()>node.getAngle())
						angle = (node.getAngle()+2*Math.PI)*animationProgress + node.getOldAngle()*(1-animationProgress);
					else
						angle = node.getAngle()*animationProgress + (node.getOldAngle()+2*Math.PI)*(1-animationProgress);
				else
					angle = node.getAngle()*animationProgress + node.getOldAngle()*(1-animationProgress);
				radius = getRadiusOfRing(node.getRing())*animationProgress + getRadiusOfRing(node.getOldRing())*(1-animationProgress);
				node.setPosition((float)(Math.sin(angle)*radius), (float)(Math.cos(angle)*radius));
			}
		}
		//System.out.println("vis ended");
		setNodesInstantlyToNewPos();
		isBusy = false;
	}
	/**
	 * time of animation in nanoseconds
	 * @return time of animation in nanoseconds
	 */
	public static long getAnimationTiming() {
		return animationTiming;
	}
	/**
	 *  time of animation in nanoseconds
	 * @param animationTiming time of animation in nanoseconds
	 */
	public static void setAnimationTiming(long animationTiming) {
		Visualization.animationTiming = animationTiming;
	}
	/**
	 * ring scaling
	 * @return ring scaling
	 */
	public static float getRingScaling() {
		return ringScaling;
	}
	/**
	 * ring scaling
	 * @param ringScaling ring scaling
	 */
	public static void setRingScaling(float ringScaling) {
		Visualization.ringScaling = ringScaling;
	}
	/**
	 * the rings are disorted according to their number and ringDisort (outer rings have less distance)
	 * @return ringDisort
	 */
	public static float getRingDisort() {
		return ringDisort;
	}
	/**
	 * the rings are disorted according to their number and ringDisort (outer rings have less distance)
	 * @param ringDisort the rings are disorted according to their number and ringDisort
	 */
	public static void setRingDisort(float ringDisort) {
		Visualization.ringDisort = ringDisort;
	}
	/**
	 * gets the radius of a particular ring taking ringScaling and ringDisort into account
	 * @param Ring to get radius of
	 * @return radius
	 */
	public static double getRadiusOfRing(int Ring){
		return Math.pow(Ring*ringScaling,1/ringDisort)*ringDisort;
	}
	/**
	 * returns the focus node
	 * @return focus node
	 */
	public static Node getFocusNode() {
		return focusNode;
	}
	/**
	 * sets the focus node of the radial layout
	 * @param focusNode
	 */
	public static void setFocusNode(Node focusNode) {
		graphProcessing.setFocusNode(focusNode);
		Visualization.focusNode = focusNode;
	}
	/**
	 * returns the node at a given euclidian position if one is there and run() is not running, otherwise returns null
	 * @param pos position to search
	 * @return node at the position, otherwise null
	 */
	public static Node getNodeAt(Vector2 pos){
		if(isBusy)
			return null;
		//System.out.println("Clicked at x: "+pos.x+"; y: "+pos.y);
		DepthFirstIterator iter = graphProcessing.iteratorDepthFirst();
		while(iter.hasNext()){
			Node node = iter.next();
			if(node.getPosition().distance(pos) < node.getSize()/2)
				return node;
		}
		return null;
	}
	/**
	 * returns the GraphProcessing of the Visualization
	 * @return graphProcessing
	 */
	public static GraphProcessing getGraphProcessing() {
		return graphProcessing;
	}
	/**
	 * sets the position of the node instantly to the new one
	 */
	public static void setNodesInstantlyToNewPos(){
		Iterator<Node>iterator = graphProcessing.iteratorDepthFirst();
		while(iterator.hasNext()){
			Node node = iterator.next();
			double radius = getRadiusOfRing(node.getRing());
			double angle = node.getAngle();
			node.setPosition((float)(Math.sin(angle)*radius), (float)(Math.cos(angle)*radius));
		}
	}
}
